package org.jetnuke.modules.calendar.utils;

import java.text.DateFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import org.apache.velocity.VelocityContext;
import org.jetnuke.entities.CalendarEvent;

public class CalendarUtils {
	private CalendarUtils() {
	}

	public static void putCommonObjects(VelocityContext vc) {
		Locale locale = (Locale) vc.get("_locale");
		String[] MONTHS = new DateFormatSymbols(locale).getMonths();
		vc.put("MONTHS", Arrays.asList(MONTHS));
		ArrayList years = new ArrayList();
		GregorianCalendar gc = new GregorianCalendar();
		int year = gc.get(Calendar.YEAR);
		for (int i = 0; i < 5; i++) {
			years.add(new Integer(year + i));
		}
		vc.put("next_years", years);
		String[] DAYS = new DateFormatSymbols(locale).getShortWeekdays();
		String[] dstArray = new String[7];
		System.arraycopy(DAYS, 1, dstArray, 0, 7);
		vc.put("DAYS", Arrays.asList(dstArray));
	}
	public static List getActiveRecurringEvent(List calendarEvents, GregorianCalendar day_){
		List realRecurringEvents=new ArrayList();
		Iterator it=calendarEvents.iterator();
		while(it.hasNext()){
			CalendarEvent ceTmp=(CalendarEvent)it.next();
			Date time=day_.getTime();
			if(ceTmp.getEXPIRATION()!=null && ceTmp.getEXPIRATION().before(time))
				continue;
			if(ceTmp.getSTART_DATE().after(time))
					continue;
			if(ceTmp.getRECURRING()==1){
				GregorianCalendar gcTmp=new GregorianCalendar();
				gcTmp.setTime(ceTmp.getSTART_DATE());
				if(ceTmp.getFREQ()==1){ //Daily Freq and not expired
					realRecurringEvents.add(ceTmp);
				}
				else if(ceTmp.getFREQ()==2){ //Only if same day of week
					if(gcTmp.get(Calendar.DAY_OF_WEEK)==day_.get(Calendar.DAY_OF_WEEK)){
						realRecurringEvents.add(ceTmp);
					}
				}
				else if(ceTmp.getFREQ()==3){ //Only if same day of month
					if(gcTmp.get(Calendar.DAY_OF_MONTH)==day_.get(Calendar.DAY_OF_MONTH)){
						realRecurringEvents.add(ceTmp);
					}
				}
				else if(ceTmp.getFREQ()==5){ //Only if same day of year
					if(gcTmp.get(Calendar.DAY_OF_MONTH)==day_.get(Calendar.DAY_OF_MONTH)&& gcTmp.get(Calendar.MONTH)==day_.get(Calendar.MONTH)){
						realRecurringEvents.add(ceTmp);
					}
				}else if(ceTmp.getFREQ()==6){ //Mon, Wed, Fri
					int dayOfWeek=gcTmp.get(Calendar.DAY_OF_WEEK);
					if(dayOfWeek==Calendar.MONDAY || dayOfWeek==Calendar.WEDNESDAY || dayOfWeek==Calendar.FRIDAY){
						realRecurringEvents.add(ceTmp);
					}
				}
				else if(ceTmp.getFREQ()==7){ //Tue, Thu
					int dayOfWeek=gcTmp.get(Calendar.DAY_OF_WEEK);
					if(dayOfWeek==Calendar.TUESDAY || dayOfWeek==Calendar.THURSDAY ){
						realRecurringEvents.add(ceTmp);
					}
				}
				else if(ceTmp.getFREQ()==8){ //Mon Thru Fri
					int dayOfWeek=gcTmp.get(Calendar.DAY_OF_WEEK);
					if(dayOfWeek==Calendar.MONDAY || dayOfWeek==Calendar.THURSDAY || dayOfWeek==Calendar.FRIDAY){
						realRecurringEvents.add(ceTmp);
					}
				}
				else if(ceTmp.getFREQ()==9){ //Sat ,Sun
					int dayOfWeek=gcTmp.get(Calendar.DAY_OF_WEEK);
					if(dayOfWeek==Calendar.SATURDAY || dayOfWeek==Calendar.SUNDAY ){
						realRecurringEvents.add(ceTmp);
					}
				}
				
			}else if (ceTmp.getRECURRING()==2){
				int wf=ceTmp.getWF();
				// wf==1 First
				// wf==1 Second
				// wf==1 Third
				// wf==1 Fourth
				// wf==-1 Last
				int wd=ceTmp.getWD(); //Day of Week
				int wx=ceTmp.getWX();
				//1"  every month
				//2 other month

				//3 every 3 months
				//4 every 4  months
				//6 every 6 months
				//7 every year
				if(day_.get(Calendar.DAY_OF_WEEK)!=(wd+1))
					continue;
				if(wf==-1)
					wf=5;
				GregorianCalendar test=getNthOfMonth(wf,wd+1,day_.get(Calendar.MONTH),day_.get(Calendar.YEAR));
				if(test.equals(day_)){
					if(wx==1){
						realRecurringEvents.add(ceTmp);
					}else {
						GregorianCalendar gcTmp=new GregorianCalendar();
						gcTmp.setTime(ceTmp.getSTART_DATE());
						int month1=day_.get(Calendar.MONTH);
						int month2=gcTmp.get(Calendar.MONTH);
						if(wx==2){
							if(Math.abs(month1-month2)%2==0)
								realRecurringEvents.add(ceTmp);
						}
						else if (wx==3){
							if(Math.abs(month1-month2)%3==0)
								realRecurringEvents.add(ceTmp);
						}else if (wx==4){
							if(Math.abs(month1-month2)%4==0)
								realRecurringEvents.add(ceTmp);
						}else if (wx==6){
							if(Math.abs(month1-month2)%6==0)
								realRecurringEvents.add(ceTmp);
						}else if (wx==7){
							if(month1==month2)
								realRecurringEvents.add(ceTmp);
						}
					}
				}
				
			}
		}
		return realRecurringEvents; 
	}

	//Inspired by no.geosoft.cc.util.Day class
	public static GregorianCalendar getNthOfMonth(int n, int dayOfWeek,
			int month, int year) throws ArrayIndexOutOfBoundsException {
		
		// Validate the dayOfWeek argument
		if (dayOfWeek < 1 || dayOfWeek > 7)
			throw new ArrayIndexOutOfBoundsException(dayOfWeek);

		GregorianCalendar first = new GregorianCalendar(year, month, 1);

		int offset = dayOfWeek - first.get(Calendar.DAY_OF_WEEK);
		if (offset < 1)
			offset = 7 + offset;

		int dayNo = (n - 1) * 7 + offset + 1;

		return dayNo > first.getActualMaximum(Calendar.DAY_OF_MONTH) ? null
				: new GregorianCalendar(year, month, dayNo);
	}

	public static GregorianCalendar getFirstOfMonth(int dayOfWeek, int month,
			int year) {
		return getNthOfMonth(1, dayOfWeek, month, year);
	}

	public static GregorianCalendar getLastOfMonth(int dayOfWeek, int month,
			int year) {
		GregorianCalendar day = getNthOfMonth(5, dayOfWeek, month, year);
		return day != null ? day : getNthOfMonth(4, dayOfWeek, month, year);
	}
	public static GregorianCalendar getFirstDayOfWeek(int year, int week){
		GregorianCalendar firstDayWeek=new GregorianCalendar();
		firstDayWeek.set(Calendar.YEAR,year);
		firstDayWeek.set(Calendar.WEEK_OF_YEAR,week);
		firstDayWeek.set(Calendar.DAY_OF_WEEK,1);
		firstDayWeek.set(Calendar.HOUR_OF_DAY, 0);
		firstDayWeek.set(Calendar.MINUTE, 0);
		firstDayWeek.set(Calendar.SECOND, 0);
		firstDayWeek.set(Calendar.MILLISECOND, 0);
		return firstDayWeek;
	}
	public static GregorianCalendar getLastDayOfWeek(int year, int week){
		GregorianCalendar firstDayWeek=new GregorianCalendar();
		firstDayWeek.set(Calendar.YEAR,year);
		firstDayWeek.set(Calendar.WEEK_OF_YEAR,week);
		firstDayWeek.set(Calendar.DAY_OF_WEEK,7);
		firstDayWeek.set(Calendar.HOUR_OF_DAY, 0);
		firstDayWeek.set(Calendar.MINUTE, 0);
		firstDayWeek.set(Calendar.SECOND, 0);
		firstDayWeek.set(Calendar.MILLISECOND, 0);
		return firstDayWeek;
	}
	
	public static List getDaysOfWeek(int year, int week){
		List list=new ArrayList();
		GregorianCalendar firstDayWeek=getFirstDayOfWeek(year,week);
		list.add(firstDayWeek);
		
		for(int i=0;i<6;i++){
			GregorianCalendar tmp=new GregorianCalendar();
			tmp.setTimeInMillis(firstDayWeek.getTimeInMillis());
			tmp.add(Calendar.DATE, 1+i);
			list.add(tmp);
		}
		return list;
	}

	public static void main(String args[]) {
		System.out
				.println(getNthOfMonth(1, Calendar.WEDNESDAY, Calendar.FEBRUARY, 2006).getTime());
		System.out
		.println(getLastOfMonth(Calendar.SUNDAY, Calendar.DECEMBER, 2006).getTime());
		
		GregorianCalendar jan10=new GregorianCalendar(2006,Calendar.FEBRUARY,1);
		int week=jan10.get(Calendar.WEEK_OF_YEAR);
		int dayweek=jan10.get(Calendar.DAY_OF_WEEK);
		if(dayweek>	1)
			jan10.add(Calendar.DATE,-dayweek+1);
		System.out
		.println(jan10.getTime());
		System.out.println(getFirstDayOfWeek(2006,1).getTime());
		
	}

	public static List getDaysOfMonth(int year, int month) {
		List list=new ArrayList();
		GregorianCalendar firstDayMonth=getFirstDayOfMonth(year,month);
		list.add(firstDayMonth);
		int max=firstDayMonth. getActualMaximum(Calendar.DAY_OF_MONTH);
		for(int i=0;i<max-1;i++){
			GregorianCalendar tmp=new GregorianCalendar();
			tmp.setTimeInMillis(firstDayMonth.getTimeInMillis());
			tmp.add(Calendar.DATE, 1+i);
			list.add(tmp);
		}
		return list;
	}

	private static GregorianCalendar getFirstDayOfMonth(int year, int month) {
		GregorianCalendar firstDayMOnth=new GregorianCalendar();
		firstDayMOnth.set(Calendar.YEAR,year);
		firstDayMOnth.set(Calendar.MONTH,month-1);
		firstDayMOnth.set(Calendar.DAY_OF_MONTH,1);
		firstDayMOnth.set(Calendar.HOUR_OF_DAY, 0);
		firstDayMOnth.set(Calendar.MINUTE, 0);
		firstDayMOnth.set(Calendar.SECOND, 0);
		firstDayMOnth.set(Calendar.MILLISECOND, 0);
		return firstDayMOnth;
	}
}
